#include "raid.h"

// #RAID_BLOCK_SIZE 512

unsigned long blockcount(const char *filename)
{
	int fd;
	unsigned long blkcnt=0;

	fd = open(filename, O_RDONLY);
	if (fd == -1) {
		perror("open");
		exit(1);
	}
	if (ioctl(fd, BLKGETSIZE, &blkcnt) == -1) {
		perror("ioctl");
		exit(1);
	}
	close(fd);

	return blkcnt;
}

size_t parityWrite(
	int fd0, int fd1, int fd2,
	const void *buf0, const void *buf1,
	size_t count)
{

	size_t i;
	for(i = 0; i < count; i++) {
			char byte0 = ((char*)buf0)[i];
			char byte1 = ((char*)buf1)[i];
			char parity = byte0 ^ byte1;
			write(fd0, &byte0, sizeof(char));
			write(fd1, &byte1, sizeof(char));
			write(fd2, &parity, sizeof(char));
	}
	return count;
}

size_t parityRead(int fd0, int fd1, void *buf, size_t count) {
	char *buff0 = (char*)malloc(count);
	char *buff1 = (char*)malloc(count);
	char *buff = (char*)buf;
	read(fd0, buff0, count);
	read(fd1, buff1, count);
	size_t i;
	for(i = 0; i < count; i++) {
		buff[i] = buff0[i] ^ buff1[i];
	}
	return count;
}

// Write to parity disk
void do_parity()
{
	int fd0 = open("/dev/loop0", O_RDWR);
	int fd1 = open("/dev/loop1", O_RDWR);
	int fdp = open("/dev/loop2", O_RDWR);
	if (fd0 == -1 || fd1 == -1 || fdp == -1) {
		perror("open");
		exit(1);
	}

	unsigned long block_count = blockcount("/dev/loop0");
	int size = RAID_BLOCK_SIZE * block_count;
	char *buff0 = (char*)malloc(size);
	char *buff1 = (char*)malloc(size);
	if(read(fd0, buff0, size) == -1) {
		perror("read");
		exit(1);
	}
	if(read(fd1, buff1, size) == -1) {
		perror("read");
		exit(1);
	}
	parityWrite(fd0, fd1, fdp, buff0, buff1, size);
	close(fd0);
	close(fd1);
	close(fdp);
}

/**
 * Takes lost device ID as argument and recovers it
 */
void do_recovery(int device_id)
{
	const char *found_device;
	const char *lost_device;
	// If device 0 is lost recover it using 1 and parity
	// Otherwise recover recover device 1 using 0 and parity
	if (device_id == 0) {
		lost_device = "/dev/loop0";
		found_device = "/dev/loop1";
	} else {
		lost_device = "/dev/loop1";
		found_device = "/dev/loop0";
	}
	int fdf = open(found_device, O_RDWR, 0666); // Open the data device
	int fdl = open(lost_device, O_RDWR, 0666); // Open the data device
	int fdp = open("/dev/loop2", O_RDWR, 0666); // Open the parity device
	if (fdf == -1 || fdp == -1 || fdl == -1) {
		perror("open");
		exit(1);
	}

	unsigned long block_count = blockcount("/dev/loop0");
	int size = RAID_BLOCK_SIZE * block_count;
	char *buff0 = (char*)malloc(size);
	size_t found_size = parityRead(fdf, fdp, buff0, size);
	//printf ("length: %d and data: %s\n", strlen(buff0), buff0);
	if(write(fdl, buff0, size) == -1) {
		perror("write");
		exit(1);
	}
	close(fdf);
	close(fdl);
	close(fdp);
}

